package npda;

import org.junit.jupiter.api.Test;
import pda.PDARule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author haoly
 * @title LexicalAnalyzerTest
 * @date 2025/4/22 14:52
 * @description TODO
 */
public class LexicalAnalyzerTest {
    @Test
    public void testAnalyze(){
        String input = "y = x * 7";
        LexicalAnalyzer analyzer = new LexicalAnalyzer(input);
        assertEquals(Arrays.asList("v", "=", "v", "*", "n"), analyzer.analyze());

        input = "while (x < 5) { x = x * 3 }";
        analyzer = new LexicalAnalyzer(input);
        assertEquals(Arrays.asList("w", "(", "v", "<", "n", ")", "{", "v", "=", "v", "*", "n", "}"),
                analyzer.analyze());


        input = "if (x < 10) { y = true; x = 0 } else { do-nothing }";
        analyzer = new LexicalAnalyzer(input);
        assertEquals(Arrays.asList("i", "(", "v", "<", "n", ")", "{", "v", "=", "b", ";", "v", "=", "n", "}", "e", "{", "d", "}"),
                analyzer.analyze());


    }

    @Test
    public void testCfg2Npda(){
        // 1. 建立映射

        // 2. 初始化规则
        PDARule<Integer> startRule = new PDARule<>(1, null, 2, '$', "S$");
        System.out.println("startRule="+startRule);
        // 3. 文法展开规则
        List<PDARule<Integer>> symbolRules = Arrays.asList(
                // <statement> ::= <while> | <assign>
                new PDARule<>(2, null, 2, 'S', "W"),
                new PDARule<>(2, null, 2, 'S', "A"),
                // <while> ::= 'w' '(' <expression> ')' '{' <statement> '}'
                new PDARule<>(2, null, 2, 'W', "w(E){S}"),
                // <assign> ::= 'v' '=' <expression>
                new PDARule<>(2, null, 2, 'A', "v=E"),
                // <expression> ::= <less-than>
                new PDARule<>(2, null, 2, 'E', "L"),
                // <less-than> ::= <multiply> '<' <less-than> | <multiply>
                new PDARule<>(2, null, 2, 'L', "M"),
                new PDARule<>(2, null, 2, 'L', "M<L"),
                // <multiply> ::= <term> '*' <multiply> | <term>
                new PDARule<>(2, null, 2, 'M', "T*M"),
                new PDARule<>(2, null, 2, 'M', "T"),
                // <term> ::= 'n' | 'v'
                new PDARule<>(2, null, 2, 'T', "n"),
                new PDARule<>(2, null, 2, 'T', "v")
        );
        System.out.println("symbolRules="+symbolRules);
        // 4. 终结符匹配规则
        List<PDARule<Integer>> tokenRules = new ArrayList<>();
        for (LexicalAnalyzer.GrammarRule grammarRule : LexicalAnalyzer.GRAMMAR ) {
            Character tokenChar = grammarRule.getToken().charAt(0);
            PDARule<Integer> rule = new PDARule<>(2, tokenChar, 2, tokenChar, "");
            tokenRules.add(rule);
        }

        System.out.println("tokenRules="+tokenRules);

        // 5. 终止条件
        PDARule<Integer> stopRule = new PDARule<>(2, null, 3, '$', "$");
        System.out.println("stopRule="+stopRule);

        List<PDARule<Integer>> rules = new ArrayList<>(Arrays.asList(startRule, stopRule));
        rules.addAll(symbolRules);
        rules.addAll(tokenRules);

        // 创建NPDA规则集
        NPDARulebook<Integer> rulebook = new NPDARulebook<>(rules);

        NPDADesign<Integer> npdaDesign = new NPDADesign<>(1, '$', Set.of(3), rulebook);

        // while (x < 5) { x = x * 3 }
        LexicalAnalyzer lexicalAnalyzer = new LexicalAnalyzer("while (x < 5) { x = x * 3 }");
        // token字符串
        List<String> tokenStringList = lexicalAnalyzer.analyze();
        System.out.println(tokenStringList);
//        StringBuilder sb = new StringBuilder();
//        for (String tokenStr : tokenStringList) {
//            sb.append(tokenStr);
//        }
//        System.out.println(sb);
        String tokenString = tokenStringList.stream().collect(Collectors.joining(""));
        System.out.println(tokenString);

        assertTrue(npdaDesign.accepts(tokenString));

        // while (x < 5 x = x * }
        lexicalAnalyzer = new LexicalAnalyzer("while (x < 5 x = x * }");
        // token字符串
        tokenStringList = lexicalAnalyzer.analyze();
        System.out.println(tokenStringList);
//        sb = new StringBuilder();
//        for (String tokenStr : tokenStringList) {
//            sb.append(tokenStr);
//        }
//        System.out.println(sb);
//        tokenString = sb.toString();
        tokenString = tokenStringList.stream().collect(Collectors.joining(""));
        System.out.println(tokenString);
        assertFalse(npdaDesign.accepts(tokenString));
    }
}
